动态规划 括号匹配(二)问题

 

   括号匹配(二)

时间限制:1000 ms  |  内存限制:65535 KB

难度:6

描述

给你一个字符串,里面只包含"(",")","[","]"四种符号,请问你需要至少添加多少个括号才能使这些括号匹配起来。

如:

[]是匹配的

([])[]是匹配的

((]是不匹配的

([)]是不匹配的

输入

第一行输入一个正整数N,表示测试数据组数(N<=10)

每组测试数据都只有一行,是一个字符串S,S中只包含以上所说的四种字符,S的长度不超过100

输出

对于每组测试数据都输出一个正整数,表示最少需要添加的括号的数量。每组测试输出占一行

样例输入

4

[]

([])[]

((]

([)]

样例输出

0

0

3

2

来源

《算法艺术与信息学竞赛》

上传者

张云聪

     AC 0.32

 

一开始用栈来做,判断是否匹配,不匹配则计数+1,计算出不匹配个数。这种算法处理不了很多数据。

然后又改成两端向内依次寻找匹配,匹配则删去然后再寻找这个匹配里面的括号。一部分匹配完后,继续在没有匹配的数据中寻找,直到所有数据都被处理。这个算法比上面的好一点,但是不能处理()()或([)(])这样的特殊数据。

最后查到了用动态规划来做,然后又设计了第三种算法,成功AC。

 

动态规划思路:

把问题分为几个阶段,分别求解,将每一次运算的结果保存在一个二维数组中,以后的求解过程可以使用之前的运算结果。

 

本题思路:

可以把字符串分解,有以下这几种情况:

1.S="("等,此时dp=1.

2.S="(S')",S需要的最小括号数=S'

3.S="S'1+S'2",S需要的最小括号数=S'1的+S'2的

 

 

1.首先,建立一个二位数组dp[][]来存储结果,dp[i][j]表示从S[i]到S[j]的最少添加括号个数。

2.那么首先可以把dp[i][j](i==j)的值存为1,因为一个字符必定需要添加一个括号。其他值先赋值为0。(阶段0)

3.然后,进行循环遍历,每阶段考虑k个长度的区间(k从1到总长),每次考虑i,j内的区间(i从0开始依次考虑),如果S[i]==S[j],说明他们是匹配的,dp[i][j]=dp[i+1][j-1](括号里面部分需要添加的最小括号数)

4.此外,该题还有分割情况(比如"[[[]]][[[]]]"需要分成两部分考虑),应依次考虑所有分割情况,如果进行了某种分割后,最多需要添加括号更少,就采用更小的值,所以有for(int k = i;k<j;k++)dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);

5.全部处理完后,dp[0][length-1]即为S[0]S[length]也就是整个字符串需要添加的最小括号数。

又该题可以看出,动态规划实际遍历了所有可能结果,其中用二维数组保存一些结果,加快了运算速度。

 

手动演算"[((](]"的结果:

源码

#define R 105
#include<stdio.h>
#include<limits.h>
int length(char s[]){
    int i=0;
    while(s[i++]!='\0');
    return i-1;
}
bool match(char a,char b){
    if(a=='('&&b==')')return 1;
    if(a=='['&&b==']')return 1;
    return 0;
}
int min(int a,int b){
    if(a<b)return a;
    else return b;
}
int main(){
    int n;
    char all[R];
    int dp[R][R];
    int i,j,k,x;
    int len;
    scanf("%d",&n);
    while(n--){
        scanf("%s",all);
        len = length(all);
        for(i=0;i<len;i++)
            for(j=0;j<R;j++)
                dp[i][j]=0;
        for(i=0;i<len;i++)
            dp[i][i]=1;
        for(k=1;k<=len;k++){//k为阶段数,表示处理长度为k的区间
            for(i=0;i<len-k;i++){
                j=i+k;
                dp[i][j]=INT_MAX;
                if(match(all[i],all[j]))
                    dp[i][j]=dp[i+1][j-1];
                for(x=i;x<j;x++)
                    dp[i][j]=min(dp[i][j],dp[i][x]+dp[x+1][j]);
                //printf("dp[%d][%d] = %d\n",i,j,dp[i][j]);
            }
        }
        printf("%d\n",dp[0][len-1]);
    }
    return 0;
}

另外符上失败的两种算法(注意这两种是错的):

#include<stdio.h>
int length(char s[]){
    int i=0;
    while(s[i++]!='\0');
    return i-1;
}
int main(){
    int n,l,i,x,number;
    char all[105],S[105];
    scanf("%d",&n);
    while(n--){
        number = 0;
        x = 0;
        scanf("%s",all);
        l = length(all);
        for(i=0;i<l;i++){
            if(all[i]=='('||all[i]=='['){
                S[x] = all[i];
                x++;
            }
            else if(all[i]==')'&&S[x-1]=='('){
                x--;
            }
            else if(all[i]==']'&&S[x-1]=='['){
                x--;
            }
            else{
                number++;
            }
        }
        number += x;
        printf("%d\n",number);
    }
    return 0;
}
//该算法不足,放弃
#include<stdio.h>
int length(char s[]){
    int i=0;
    while(s[i++]!='\0');
    return i-1;
}
char match(char a){
    if(a == '(')
        return ')';
    else if(a == '[')
        return ']';
    else
        return '\0';
}
int main(){
    int n,l;
    int a,number;
    int i,j;
    char all[105];
    scanf("%d",&n);
    while(n--){
        number = 0;
        scanf("%s",all);
        l = length(all);
        a = l - 1;
        for(i=0;i<l;i++){
            //printf("i==%d",i);
            if(all[i]==')'||all[i]==']'){
                number++;
            }
            else if(all[i]=='('||all[i]=='['){
                for(j=a;j>=i;j--){
                    if(all[j]==match(all[i])){
                        //printf("match %d == %d\n",i,j);
                        all[j]='\0';
                        a=j-1;
                        break;
                    }
                    else if(i==j){
                        number++;
                        break;
                    }
                }
            }
            else if(i>=a){
                while(all[++i]=='\0');
                i--;
                a = i;
                while(all[++a]!='\0'&&a<l);
                a--;
            }
        }
        printf("%d\n",number);
    }
    return 0;
}

 问题地址传送门

资料传送门

  • 11
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
好的,我了解了您的问题。为了解决这个问题,我们可以使用动态规划算法。 具体来说,我们可以使用一个维的 dp 数组,其中 dp[i][j] 表示从 i 到 j 这个区间中添加最少的括号数。根据这个定义,我们可以得到状态转移方程: - 如果 s[i] 和 s[j] 匹配,那么 dp[i][j] = dp[i+1][j-1]。 - 对于任意 k,使得 i ≤ k < j,我们可以将区间 [i, j] 分成两部分,即 [i, k] 和 [k+1, j],并且分别求解它们的最小括号数,然后将它们相加再加上一个括号即可,即 dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j])。 最后,当 dp[0][n-1] 等于最小括号数时,我们可以使用类似于回溯的方法,从 dp 数组中逆推出一个可能的解。 下面是 Python 代码实现: ```python def add_min_parenthesis(s): n = len(s) dp = [[float('inf')] * n for _ in range(n)] for i in range(n): dp[i][i] = 1 for l in range(2, n+1): for i in range(n-l+1): j = i + l - 1 if s[i] == "(" and s[j] == ")" or s[i] == "[" and s[j] == "]" or s[i] == "{" and s[j] == "}": dp[i][j] = dp[i+1][j-1] for k in range(i, j): dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j]) res = [] i, j = 0, n-1 while i <= j: if s[i] == "(" and s[j] == ")" and dp[i][j] == dp[i+1][j-1]: res.extend([s[i], s[j]]) i += 1 j -= 1 elif s[i] == "[" and s[j] == "]" and dp[i][j] == dp[i+1][j-1]: res.extend([s[i], s[j]]) i += 1 j -= 1 elif s[i] == "{" and s[j] == "}" and dp[i][j] == dp[i+1][j-1]: res.extend([s[i], s[j]]) i += 1 j -= 1 elif dp[i][j] == dp[i+1][j] + 1: res.extend([s[i], ")"]) i += 1 else: res.extend(["(", s[j]]) j -= 1 return "".join(res) ``` 使用该函数,您可以输入任意括号字符串,它都会返回该字符串的最小括号数,并输出改进后的括号字符串

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值